<template>
  <form class="page page-rename-by-excel">
    <h4 class="text-center">基于Excel数据重命名文件</h4>
    <div class="fieldset-options row">
      <div class="col-9">
        <fieldset>
          <legend>参数设置</legend>
          <div class="form-group row-flex" align="center">
            <div class="row-flex-prefix">EXCEL文件</div>
            <div class="row-flex-main">
              <input class="form-control" type="text" v-model="excelPath" placeholder="请选择Excel文件，用于关联待改名的文件，且需包含新文件名的所需信息" />
            </div>
            <div class="row-flex-suffix"><x-button @click="setExcelPath">选择...</x-button></div>
          </div>
          <div class="form-group row-flex" align="center">
            <div class="row-flex-prefix">改名规则</div>
            <div class="row-flex-main">
              <div class="input-group">
                <div class="input-group-prepend">
                  <span class="input-group-text">旧名称</span>
                </div>
                <input class="form-control" type="text" v-model.trim="oldNameReg" placeholder="格式:*${列号}*，支持多个列号"/>
                <div class="input-group-prepend">
                  <span class="input-group-text">新名称</span>
                </div>
                <input class="form-control" type="text" v-model.trim="newNameReg" placeholder="格式:*${列号}*，如：${1}_${2}.png"/>
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="col-3">
        <fieldset class="flex-center">
          <legend>操作</legend>
          <div class="form-group">
            <x-button type="default" block @click="preview" :disabled="loadingCount > 0">预览</x-button>
          </div>
          <div class="form-group">
            <x-button type="primary" block @click="execute" :loading="loadingCount > 0">执行修改</x-button>
          </div>
        </fieldset>
      </div>
    </div>
    <fieldset class="fieldset-content">
      <legend>文件列表</legend>
      <div class="row-flex" align="center">
        <div class="row-flex-main text-left">共 {{filesList.length}} 个文件</div>
        <div class="row-flex-suffix">
          <button type="button" class="btn btn-link" @click="clearFilesList" title="清空">
            <i class="fa fa-trash-o"></i>
          </button>
        </div>
      </div>
      <div class="table-header" :style="thStyles">
        <table>
          <tbody>
            <tr>
              <th v-for="(v,i) in tableHeaders" :key="`th_${i}`" :style="{width: v.width}">{{v.label}}</th>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="table-body" @drop.prevent="filesDrop" @dragover.prevent>
        <table ref="table">
          <tbody>
            <tr v-for="(v,i) in filesList" :key="`file_${i}`">
              <template v-for="(item,index) in tableHeaders">
                <td :style="{width: item.width}" :key="`td_${i}_${index}`">
                  <button v-if="item.value==='action'" type="button" class="btn btn-link" @click="deleteFileItem(i)" title="删除">
                    <i class="fa fa-close"></i>
                  </button>
                  <span v-else>{{v[item.value] || ''}}</span>
                </td>
              </template>
            </tr>
            <tr v-if="filesList.length===0" class="nodata-tr"><td>将待处理的文件拖动到此处</td></tr>
          </tbody>
        </table>
      </div>
    </fieldset>
  </form>
</template>

<script>
const fs = global.require('fs')
const path = global.require('path')
const {remote} = global.require('electron')
const {dialog} = remote
const selfWindow= remote.getCurrentWindow()
let nodexlsx = global.nodexlsx || null

export default {
  name: 'rename-by-excel',
  data () {
    return {
      excelPath: '',
      excelData: [],
      filesList: [],
      tasksList: [],
      oldNameReg: '',
      newNameReg: '',
      fileNameDict: {},
      tableHeaders: [
        {label: '原文件名', value: 'oldName', width: '30%'},
        {label: '新文件名', value: 'newName', width: '30%'},
        {label: '状态', value: 'status', width: '20%'},
        {label: '操作', value: 'action', width: '20%'},
      ],
      thStyles: {},
      loadingCount: 0
    }
  },
  computed: {
    filePathsList () {
      return this.filesList.map(v => v.path)
    }
  },
  mounted () {
    nodexlsx = this.$sharedObject('nodexlsx')
  },
  methods: {
    // 选择Excel文件
    setExcelPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择文件',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openFile'],
        filters: [ { name: 'Doc', extensions: ['xlsx', 'xls'] } ]
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.excelPath = filePaths[0]
        this.getExcelData()
      }
    },
    // 获取Excel数据信息
    getExcelData () {
      let _this = this
      if (!fs.existsSync(_this.excelPath)) {
        _this.$toast('Excel文件不存在')
        return
      }
      _this.excelData = []
      _this.loadingCount++
      // 先让loading渲染，下一任务执行解析，避免页面假死
      setTimeout(() => {
        try {
          let _excelData = nodexlsx.parse(_this.excelPath)
          _this.excelData = (_excelData[0] || {}).data || []
        } catch(err) {
          console.log(err)
          _this.$Message.error(`解析${_this.excelPath}出错，请检查Excel格式后重试！`)
        }
        _this.loadingCount--
      }, 0)
    },
    // 拖拽待处理文件
    filesDrop (e) {
      const filesList = e.dataTransfer.files
      if (!filesList) return
      for (let i = 0; i < filesList.length; i++) {
        const file = filesList[i]
        if (!this.filePathsList.includes(file.path)) {
          let fileStat = fs.statSync(file.path)
          let oldName = path.basename(file.path) || file.name
          this.filesList.push({
            path: file.path,
            oldName,
            newName: '',
            dirName: path.dirname(file.path),
            status: '',
            updateTime: fileStat.mtime,
            createTime: fileStat.birthtime,
          })
        }
      }
      this.checkScroll()
    },
    // 删除条目
    deleteFileItem (index) {
      this.filesList.splice(index, 1)
      this.checkScroll()
    },
    checkScroll () {
      this.$nextTick(() => {
        if (!this.$refs.table) return
        let tableHeight = this.$refs.table.clientHeight
        let tableParentHeight = this.$refs.table.parentNode.clientHeight
        if (tableHeight > tableParentHeight) {
          this.thStyles = {overflow: 'hidden scroll'}
        } else {
          this.thStyles = {overflow: 'hidden'}
        }
      })
    },
    // 清空待处理文件
    clearFilesList () {
      this.filesList = []
      this.tasksList = []
      this.thStyles = {}
      this.loadingCount = 0
    },
    // 生成文件名字典
    excelData2nameDict () {
      this.fileNameDict = {}
      let oldNameColNum = this.getVariableArr(this.oldNameReg)
      let newNameColNum = this.getVariableArr(this.newNameReg)
      this.excelData.forEach(v => {
        let key = this.oldNameReg
        let val = this.newNameReg
        oldNameColNum.forEach(i => {
          while (key.indexOf('${' + i + '}') > -1) {
            key = key.replace('${' + i + '}', v[i])
          }
        })
        newNameColNum.forEach(i => {
          while (val.indexOf('${' + i + '}') > -1) {
            val = val.replace('${' + i + '}', v[i])
          }
        })
        if (key && val) {
          this.fileNameDict[key] = val
        }
      })
    },
    // 预览
    preview () {
      if (!this.oldNameReg) {
        this.$toast('旧文件名模板不能为空')
        return
      }
      if (!this.newNameReg) {
        this.$toast('新文件名模板不能为空')
        return
      }
      this.excelData2nameDict()
      let filesList = this.$jstools.deepCopy(this.filesList)
      this.filesList = filesList.map(v => {
        v.newName = this.fileNameDict[v.oldName] || ''
        v.status = ''
        return v
      })
    },
    // 执行修改
    execute () {
      this.preview()
      this.filesList.forEach((v, i) => {
        let newPath = path.join(v.dirName, v.newName)
        if (v.oldName === v.newName || !v.newName) return
        this.loadingCount++
        this.tasksList.push(
          this.rename(v.path, newPath).then(() => {
            this.$set(this.filesList[i], 'status', '修改成功')
            this.$set(this.filesList[i], 'oldName', v.newName)
            this.$set(this.filesList[i], 'newName', '')
            this.$set(this.filesList[i], 'path', newPath)
          }).catch((err) => {
            console.log(err)
            this.$set(this.filesList[i], 'status', '修改失败')
          }).finally(() => {
            this.loadingCount--
          })
        )
      })
      Promise.all(this.tasksList).finally(() => {
        this.tasksList = []
      })
    },
    // 重命名
    rename (from, to) {
      return new Promise((resolve, reject) => {
        fs.rename(from, to, (err) => {
          if (err) {reject(err)}
          else {resolve()}
        })
      })
    },
    // 根据模板获取用到的变量集合
    getVariableArr (template) {
      template = String(template || '')
      let result = []
      let regex = /\$\{(.+?)\}/g
      result = [...new Set(template.match(regex) || [])]
      result = result.map(v => {
        let _arr = v.match(/\$\{(.+?)\}/)
        return _arr ? _arr[1] : ''
      })
      return result.filter(v => !!v)
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.page-rename-by-excel {
	display: flex;
	flex-direction: column;
  .row-flex-prefix {
    text-align: left;
  }
  fieldset {
    margin-top: 1em;
    .row-flex-prefix {width: 5em;}
  }
	.fieldset-content {
		height: 0;
		flex-grow: 1;
    .table-header,
    .table-body {
      table {
        width: 100%;
        border: 1px solid @borderGrey;
        tr {height: 2em;}
        th, td {
          border: 1px solid @borderGrey;
          text-align: center;
          vertical-align: middle;
        }
        .nodata-tr {
          height: 100%;
          font-size: 16px;
          color: #999;
        }
      }
    }
    .table-body {
      height: calc(100% - 6.5em);
      overflow: auto;
      table {
        min-height: 100%;
      }
    }
  }
  .fieldset-options {
    fieldset {
      height: calc(100% - 1em);
    }
  }
}
</style>
